/*
 *  Copyright 2010 johan.
 * 
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 * 
 *       http://www.apache.org/licenses/LICENSE-2.0
 * 
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *  under the License.
 */
package nu.zoom.catonine.desktop.impl;

import java.awt.Component;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;

import javax.swing.ImageIcon;
import javax.swing.JButton;

import nu.zoom.catonine.desktop.DelayedCursorSwitcher;
import nu.zoom.catonine.desktop.FileOpener;
import nu.zoom.catonine.desktop.FileReopener;
import nu.zoom.catonine.desktop.TailFrameTracker;
import nu.zoom.catonine.desktop.rule.StyleRulesPlugIn;
import nu.zoom.catonine.fontchooser.FontChooserFactory;
import nu.zoom.catonine.prefs.Preferences;
import nu.zoom.catonine.prefs.Preferences.WindowOpenMode;
import nu.zoom.catonine.stylerule.StyleRules;
import nu.zoom.catonine.swing.tail.StyleRuleEditCallback;
import nu.zoom.catonine.swing.tail.TailPane;
import nu.zoom.gal.error.ErrorReporter;
import nu.zoom.gal.progress.Progress;
import nu.zoom.swing.desktop.Workbench;
import nu.zoom.swing.desktop.WorkbenchFrame;
import nu.zoom.swing.desktop.WorkbenchFrameListener;
import nu.zoom.swing.desktop.common.BackendException;
import nu.zoom.swing.desktop.common.action.DetachAction;
import nu.zoom.swing.desktop.preferences.InvalidDataTypeException;
import nu.zoom.ui.Resources;
import nu.zoom.ui.Resources.ResourceNotFoundException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * An implementation for opening files for tailing.
 * 
 * @author "Johan Maasing" &lt;johan@zoom.nu&gt;
 */
public final class FileOpenerImpl implements FileOpener {

	private final Log log = LogFactory.getLog(getClass());
	private final Workbench workbench;
	private final Resources messages;
	private final Preferences preferences;
	private final StyleRulesPlugIn styleRulesPlugIn;
	private final FileReopener reOpener;
	private final DelayedCursorSwitcher cursorSwitcher;
	private final ErrorReporter errorReporter;
	private final Progress progress;
	private final FontChooserFactory fontChooserFactory;

	public FileOpenerImpl(final Workbench workbench, final Resources messages,
			final Preferences preferences,
			final StyleRulesPlugIn styleRulesPlugIn,
			final FileReopener reOpener,
			final DelayedCursorSwitcher cursorSwitcher,
			final ErrorReporter errorReporter, final Progress progress,
			final FontChooserFactory fontChooserFactory) {
		this.workbench = workbench;
		this.preferences = preferences;
		this.styleRulesPlugIn = styleRulesPlugIn;
		this.messages = messages;
		this.reOpener = reOpener;
		this.cursorSwitcher = cursorSwitcher;
		this.errorReporter = errorReporter;
		this.progress = progress;
		this.fontChooserFactory = fontChooserFactory;
	}

	public void openFile(final File file) throws ResourceNotFoundException {
		log.trace("Opening selected file");

		final ArrayList<Component> additionalToolbarComponents = new ArrayList<Component>();
		final JButton detachToggle = new JButton();
		additionalToolbarComponents.add(detachToggle);
		final StyleRuleEditCallback editStyleRuleCallback = new StyleRuleEditCallback() {

			@Override
			public void editStyleRules(StyleRules styleRules) {
				try {
					styleRulesPlugIn.editStyleRules(styleRules);
				} catch (ResourceNotFoundException e) {
					errorReporter.reportError(e);
				}
			}

			@Override
			public void createSimpleRule(StyleRules styleRules,
					String selectedText) {
				try {
					styleRulesPlugIn.createSimpleRule(styleRules, selectedText);
				} catch (ResourceNotFoundException e) {
					errorReporter.reportError(e);
				}
			}
		};
		final TailPane pane = new TailPane(messages,
				additionalToolbarComponents, preferences, cursorSwitcher,
				errorReporter, progress, editStyleRuleCallback,
				this.fontChooserFactory.getDefaultFont());
		final TailPane2StyleRulesPlugIn listenerBridge = new TailPane2StyleRulesPlugIn(
				pane);
		try {
			log.trace("Setting style rules manager");
			pane.setStyleRulesManager(styleRulesPlugIn.getStyleRulesManager());
			styleRulesPlugIn.addListener(listenerBridge);
			log.trace("Setting file to tail");
			pane.setFile(file);
			log.trace("Creating workbench frame");
			final String prefskey = "Svansprogram.file." + file.getName();
			final WindowOpenMode windowOpenMode = preferences
					.getWindowOpenMode();
			final WorkbenchFrame frame;
			switch (windowOpenMode) {
			case Detached:
				log.debug("Preferences say to force frame to detach");
				frame = workbench.createWorkbenchFrame(prefskey, pane, null,
						true, true, true);
				break;
			case Attached:
				log.debug("Preferences say to force frame to attach");
				frame = workbench.createWorkbenchFrame(prefskey, pane, null,
						true, true, false);
				break;
			default:
				log.debug("Preferences say to use remembered/default attach/detach mode");
				frame = workbench.createWorkbenchFrame(prefskey, pane, null,
						true, true);
				break;
			}
			frame.addFrameListener(new TailerStopper(pane));
			frame.addFrameListener(new TailFrameTracker(reOpener, file));
			frame.setTitle(createTitle(file));

			final ImageIcon icon = messages.getIcon("nu.zoom.catonine.icon");
			frame.setFrameIcon(icon);

			detachToggle.setAction(new DetachAction(frame) {

				private static final long serialVersionUID = 1L;

				@Override
				protected void setAttachedLook() {
					try {
						setName(messages.getMessage("menu.window.detach"));
						setToolTip(messages
								.getMessage("menu.window.detach.tooltip"));
					} catch (ResourceNotFoundException ex) {
						workbench.getErrorReporter().reportError(ex);
					}
				}

				@Override
				protected void setDetachedLook() {
					try {
						setName(messages.getMessage("menu.window.attach"));
						setToolTip(messages
								.getMessage("menu.window.attach.tooltip"));
					} catch (ResourceNotFoundException ex) {
						errorReporter.reportError(ex);
					}
				}
			});

			log.trace("Showing workbench frame");
			frame.setVisible(true);
			final String configurationUUID = getPreferredConfigurationUUID(file);
			if (configurationUUID != null) {
				pane.setPreferredConfiguration(configurationUUID);
			}
			pane.setGutterVisible(preferences.isGutterForFile(file));

			workbench.getStatusbar().setMessage(
					messages.format("nu.zoom.catonine.opened",
							file.getAbsolutePath()));
			pane.start();
		} catch (Exception e1) {
			log.error("Unable to create tail pane", e1);
			String message = messages.getMessage("nu.zoom.catonine.open.fail");
			errorReporter.reportError(message, e1);
		}
	}

	private String createTitle(File file) {
		File absoluteFile = file.getAbsoluteFile();
		String title = absoluteFile.getName() + "@"
				+ absoluteFile.getParentFile().toString();
		return title;
	}

	private String getPreferredConfigurationUUID(final File file)
			throws InvalidDataTypeException, BackendException {
		if (file == null) {
			throw new NullPointerException("File may not be null");
		}
		final String uuid = preferences.getPreferredUUIDForFile(file);
		log.debug("File: " + file.getName()
				+ " is associated with configuration UUID: " + uuid);
		return uuid;

	}

	class TailerStopper implements WorkbenchFrameListener {

		TailPane pane;

		public TailerStopper(TailPane pane) {
			super();
			this.pane = pane;
		}

		@Override
		public void frameClosed(WorkbenchFrame frame) {
			// Nothing to do here
		}

		@Override
		public void frameWillDispose(WorkbenchFrame frame) {
			try {
				log.trace("Frame will dispose, stopping tail pane");
				pane.stop();
			} catch (IOException e) {
				log.error("Error stopping tail pane", e);
			}
		}

		@Override
		public void frameDetached(WorkbenchFrame frame) {
		}

		@Override
		public void frameAttached(WorkbenchFrame frame) {
		}
	}
}
